﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.IO.Ports;
using System.Text.RegularExpressions;
using Microsoft.Win32;
using System.Threading;

namespace JBus_communication
{    
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        
        /*****************************公用變數區******************************/
        
        //用來更新UI的委派
        public delegate void UIDelegate();

        SerialPort comPort;
        JBus testJBus;
        int numBase = 10; //資料的基底
        private string gpattern = @"[0-9]";
        const ushort ADDRESS_MIN = 0xE000;
        const ushort ADDRESS_MAX = 0xE33F;
        const int ADDRESS_MAP_SIZE = ADDRESS_MAX - ADDRESS_MIN + 1;

        //命令參數的List
        List<command4JBus> commandList = new List<command4JBus>(); 

        int comNum = 0; //送出的命令數量
        int errNum = 0; //送出命令後回傳失敗的數量

        /*********************************************************************/

        private void Form1_Load(object sender, EventArgs e)
        {

            //使3個MyRichTextBox垂直滾動同步
            richtxbAddress.Synchronized1 = richtxbData;
            richtxbAddress.Synchronized2 = numBox;
            richtxbData.Synchronized1 = richtxbAddress;
            richtxbData.Synchronized2 = numBox;
            numBox.Synchronized1 = richtxbAddress;
            numBox.Synchronized2 = richtxbData;

            //先搜尋現有的port並建立JBus通訊物件
            this.cbxPort.Items.AddRange(FindAllComPortInPC());
            if (cbxPort.Items.Count != 0)
            {
                cbxPort.SelectedIndex = 0;
                byte slaveNum = Convert.ToByte(cbxSlave.SelectedItem);
                string comportString = Convert.ToString(cbxPort.SelectedItem);
                comPort = new SerialPort(comportString, 57600, Parity.None, 8, StopBits.One);
                testJBus = new JBus(slaveNum, comPort);
            }
           

            cbxSlave.SelectedIndex = 0;
            cbxNBase.SelectedIndex = 0;

            #region about dataGridView setting
            dataGridView1.ColumnCount = 16;
            DataGridViewCellStyle style = new DataGridViewCellStyle();
            {
                style.BackColor = Color.Beige;
                //style.ForeColor = Color.Brown;
            }
            dataGridView1.AlternatingRowsDefaultCellStyle = style;
            //按F2才可以編輯儲存格
            dataGridView1.EditMode = DataGridViewEditMode.EditOnF2;
            dataGridView1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCells;
            dataGridView1.GridColor = SystemColors.ActiveBorder;

            //加入行
            for (int i = 0; i < 16; i++)
            {
                dataGridView1.Columns[i].Name = Convert.ToString(i, 16).ToUpper();
                dataGridView1.Columns[i].Width = 50;
                //禁止對columns排序
                dataGridView1.Columns[i].SortMode = DataGridViewColumnSortMode.NotSortable;
            }


            //加入列
            const int S_ADDRESS = 0xE00;
            const int E_ADDRESS = 0xE33;
            dataGridView1.Rows.Add(E_ADDRESS - S_ADDRESS + 1);
            for (int i = S_ADDRESS; i <= E_ADDRESS; i++)
            {
                dataGridView1.Rows[i - S_ADDRESS].HeaderCell.Value
                    = Convert.ToString(i, 16).ToUpper();
                //dataGridView1.Rows[i - S_ADDRESS].Height = 3;
            }

            dataGridView1.AllowUserToAddRows = false;
            dataGridView1.AllowUserToDeleteRows = false;


            #endregion

            ThreadPool.QueueUserWorkItem(new WaitCallback(sendCommand));
        }// end form1_load

        //輪詢commandList A執行緒
        private void sendCommand(object xx)
        {
            while (true)
            {
                //如果沒有命令的話就把port關掉 再睡一下
                if (commandList.Count == 0)
                {
                    if (testJBus.port.IsOpen) { testJBus.port.Close(); }
                    Thread.Sleep(500);
                }
                else
                {
                    if (!testJBus.port.IsOpen) { testJBus.port.Open(); }
                    switch (commandList[0].command)
                    {
                        case "r":
                                readData();
                                break;
                        case "w":
                                writeData();
                            break;
                    }
                }

            }//end while(true)
        }//end sendCommand

        #region 讀取頁面的功能

        //讀取資料 A執行緒
        private void readData()
        {
            testJBus.readAddress = commandList[0].address;
            if (testJBus.readAddress + commandList[0].word2Read - 1 > ADDRESS_MAX)
            {
                //當現在開始讀的位置加上要讀的資料數大於最大定址的話就分成兩段讀
                byte cutRd2RNum1 = (byte)(ADDRESS_MAX - commandList[0].address + 1);
                byte cutRd2RNum2 = (byte)(commandList[0].word2Read - cutRd2RNum1);

                sendReadCommand(testJBus.readAddress, cutRd2RNum1);
                sendReadCommand(testJBus.readAddress, cutRd2RNum2);
            }
            else
            {
                sendReadCommand(commandList[0].address, commandList[0].word2Read);
            }//end sendReadCommand
            
            //移除剛剛做完的命令
            commandList.RemoveAt(0);

            if (btnRCommandChange.Checked)
            {
                //加一個命令到命令List裡
                command4JBus Read = new command4JBus(   
                    testJBus.readAddress,
                    byte.Parse(txbRd2RNum.Text),
                    0,
                    "r"
                );
                commandList.Add(Read);
            }//check btnRcommandChange.Checked
        }//end readData

        //送讀取的命令 A執行緒
        private void sendReadCommand(ushort add, byte num)
        {
            int readFailCount = 0;
            while (testJBus.ReadThroughJBus(add, num) == -1)
            {
                errNum++;
                readFailCount++;
                if (readFailCount > 5)
                {
                    this.Invoke(new UIDelegate(comFail));
                    return;
                }
            }
            comNum = comNum + readFailCount + 1;
            this.Invoke(new UIDelegate(setReadData2Grid));
        }//end sendReadCommand

        //把讀到的資料丟到表格裡面 順便更新UI
        private void setReadData2Grid()
        {
            ushort map2GridIndex = (ushort)(testJBus.readAddress - 0xE000);
            short tempData = 0;

            for (int byteCount = 0; byteCount < testJBus.readData.Length; byteCount += 2)
            {
                tempData = (Int16)(testJBus.readData[byteCount] << 8);
                tempData += (Int16)(testJBus.readData[byteCount + 1]);

                dataGridView1.Rows[map2GridIndex / 16].Cells[map2GridIndex % 16].Value
                                            = Convert.ToString(tempData, numBase).ToUpper();
                map2GridIndex++;
                testJBus.readAddress++;

                if (map2GridIndex > ADDRESS_MAP_SIZE) { map2GridIndex = 0; }
                if (testJBus.readAddress > ADDRESS_MAX) { testJBus.readAddress = ADDRESS_MIN; }
            }
            toolStripLabel5.Text = errNum.ToString() + "/" + comNum.ToString();
        }// end setReadData2Grid

        //只顯示表格某些列的功能(要按[按我顯示])
        private void btnSeeGrid_Click(object sender, EventArgs e)
        {
            int rowSIndex = Convert.ToInt32(txbShowSAddress.Text, 16);
            int rowEIndex = Convert.ToInt32(txbShowEAddress.Text, 16);
            DataGridViewCellStyle style = new DataGridViewCellStyle();

            rowSIndex = rowSIndex - 0xE00;
            rowEIndex = rowEIndex - 0xE00;

            //因為row[0]沒辦法隱藏...這邊是讓他看起來好像隱藏
            if (rowSIndex == 0)
            {
                style.BackColor = Color.White;
                dataGridView1.Rows[0].DefaultCellStyle = style;
            }
            else
            {
                style.BackColor = Color.Black;
                dataGridView1.Rows[0].DefaultCellStyle = style;
            }

            //這裡的r=1是因為row[0]沒辦法隱藏
            for (int r = 1; r < rowSIndex; r++)
            {
                dataGridView1.Rows[r].Visible = false;
            }

            for (int r = rowSIndex; r <= rowEIndex; r++)
            {
                dataGridView1.Rows[r].Visible = true;
            }

            for (int r = rowEIndex + 1; r < dataGridView1.Rows.Count; r++)
            {
                dataGridView1.Rows[r].Visible = false;
            }

        }

        //按刷新鈕
        private void btnRCommandChange_CheckedChanged(object sender, EventArgs e)
        {
            if (btnRCommandChange.Checked)
            {
                //介面管理
                cbxPort.Enabled = false;
                cbxSlave.Enabled = false;
                txbSAddress.Enabled = false;
                txbRd2RNum.Enabled = false;
                btnRCommandChange.Text = "[停止]";

                //先把裡面全部命令清除再加一個命令到命令List裡
                commandList.Clear();
                command4JBus firstRead = new command4JBus(
                    Convert.ToUInt16(txbSAddress.Text, 16)
                    , byte.Parse(txbRd2RNum.Text)
                    , 0
                    , "r"
                );
                commandList.Add(firstRead);
            }
            else
            {
                //介面管理
                cbxPort.Enabled = true;
                cbxSlave.Enabled = true;
                txbSAddress.Enabled = false;
                txbRd2RNum.Enabled = false;
                btnRCommandChange.Text = "[刷新]";
            }
        }// end btnRCommandChange_CheckedChanged


        #endregion

        #region 寫入頁面的功能
        //寫入
        private void btnCommand_Click(object sender, EventArgs e)
        {
            //沒有輸入資料 跳出
            if (richtxbAddress.Lines.Length == 0) { return; }

            //超過address map 就跳出
            ushort addressCount = Convert.ToUInt16(richtxbAddress.Lines[0], 16);
            if (addressCount > ADDRESS_MAX || addressCount < ADDRESS_MIN) { return; }

            string[] MyWriteAddress = new string[richtxbData.Lines.Length];
            string[] MyWriteNum = new string[richtxbData.Lines.Length];
            int counter = 0;
            foreach (string rd2write in richtxbData.Lines)
            {
                if (rd2write == "") { break; }

                command4JBus writeCommand = new command4JBus();
                writeCommand.word2Read = 0;
                writeCommand.writeData = Convert.ToInt16(rd2write, numBase);
                writeCommand.address = addressCount;
                writeCommand.command = "w";
                //加一筆寫入命令至命令List
                commandList.Add(writeCommand);

                MyWriteNum[counter] = " : " + counter.ToString() + " : ";
                MyWriteAddress[counter] += Convert.ToString(addressCount, 16).ToUpper();
                counter++;
                addressCount++;
            }
            progressBar2.Value = 0;
            progressBar2.Maximum = counter;
            richtxbAddress.Lines = MyWriteAddress;
            numBox.Lines = MyWriteNum;
        }

        //寫入資料 A執行緒
        private void writeData()
        {
            testJBus.writeAddress = commandList[0].address;
            int readFailCount = 0;
            while (testJBus.WriteThroughJBus(testJBus.writeAddress, commandList[0].writeData) == -1)
            {
                errNum++;
                readFailCount++;
                if (readFailCount > 5)
                {
                    this.Invoke(new UIDelegate(comFail));
                    return;
                }
                comNum = comNum + readFailCount + 1;
            }
            //移除剛剛做完的命令
            commandList.RemoveAt(0);
            this.Invoke(new UIDelegate(refreshWritePage));
        }

        //更新寫入頁的UI
        private void refreshWritePage()
        {
            progressBar2.Value++;
        }
        #endregion  

        //傳送命令失敗
        private void comFail()
        {
            btnRCommandChange.Checked = false;
            MessageBox.Show("通訊失敗");
        }//end comFail

        //清除鈕
        private void btnClear_Click(object sender, EventArgs e)
        {
           

            richtxbData.Lines = null;
            richtxbAddress.Lines = null;
            richtxbAddress.Text = "E000";
            numBox.Lines = null;

            comNum = 0;
            errNum = 0;
            toolStripLabel5.Text = errNum.ToString() + "/" + comNum.ToString();
            //progressBar1.Value = 0;

            dataGridView1.SelectAll();
            for (int counter = 0; counter < (dataGridView1.SelectedCells.Count); counter++)
            {
                dataGridView1.SelectedCells[counter].Value = null;
            }
            dataGridView1.ClearSelection();
        }//end btnClear_Click

        //進制轉換
        private void cbxNBase_SelectedIndexChanged(object sender, EventArgs e)
        {
            int i = 0;
            int selectBase = Convert.ToInt32(cbxNBase.SelectedItem);

            string[] temp = new string[richtxbData.Lines.Length];
            if (numBase == selectBase) { return; }

            //把現在放在richtxbData的資料改成你選的數字基底
            i = 0;
            Int16 num;
            foreach (string s in richtxbData.Lines)
            {
                if (!(Regex.IsMatch(s, gpattern))) { continue; }
                num = Convert.ToInt16(s, numBase);
                temp[i++] = Convert.ToString(num, selectBase).ToUpper();

            }
            richtxbData.Lines = temp;

            //把現在放在datagridview的資料改成你選的數字基底
            dataGridView1.SelectAll();
            string gridString;
            for (i = 0; i < (dataGridView1.SelectedCells.Count); i++)
            {
                if (dataGridView1.SelectedCells[i].Value == null) { continue; }
                gridString = dataGridView1.SelectedCells[i].Value.ToString();
                num = Convert.ToInt16(gridString, numBase);
                dataGridView1.SelectedCells[i].Value = Convert.ToString(num, selectBase).ToUpper();
            }
            dataGridView1.ClearSelection();

            
            switch (Convert.ToInt32(cbxNBase.SelectedItem))
            {
                case 10:
                    numBase = 10;
                    gpattern = @"[0-9]";
                    break;
                case 16:
                    numBase = 16;
                    gpattern = @"[0-9a-fA-F]";
                    break;
            }//end switch

        }//end cbxNBase_SelectedIndexChanged

        #region COMport & slaveNum
        // 找出電腦端所有可用的comport        
        private string[] FindAllComPortInPC()
        {
            //string aa = @"\Device\BtPort0";
            RegistryKey rkApp = Registry.LocalMachine.OpenSubKey("HARDWARE\\DEVICEMAP\\SERIALCOMM", false);
            //rkApp.SetValue("MyApp", Application.ExecutablePath.ToString() + " ablerexstart");
            if (rkApp != null)
            {
                string[] aa = rkApp.GetValueNames();
                List<string> allcomport = new List<string>();
                for (int i = 0; i < aa.Length; i++)
                {
                    //是否為藍芽
                    if (aa[i].IndexOf("BT") == -1 && aa[i].IndexOf("Bt") == -1) //有相同的為0, 沒有為-1
                        allcomport.Add(rkApp.GetValue(aa[i]).ToString());
                }
                return allcomport.ToArray();
            }
            else
                return null;
        }

        //COMport 或 SlaveNum 的選擇改變時
        private void JBus_SelectedIndexChanged(object sender, EventArgs e)
        {
            byte slaveNum = Convert.ToByte(cbxSlave.SelectedItem);
            string comportString = Convert.ToString(cbxPort.SelectedItem);
            comPort = new SerialPort(comportString, 57600, Parity.None, 8, StopBits.One);
            testJBus = new JBus(slaveNum, comPort);
        }

        //要改COMport前會先去找現在存在的port
        private void cbxPort_Click(object sender, EventArgs e)
        {
            this.cbxPort.Items.Clear();
            this.cbxPort.Items.AddRange(FindAllComPortInPC());
        }
        #endregion

        #region DataGridView的右鍵功能表

        //選到的變紅色粗體字
        private void tolsmenuHighLine_Click(object sender, EventArgs e)
        {
            DataGridViewCellStyle style = new DataGridViewCellStyle();
            {
                style.ForeColor = Color.Red;
                style.Font = new Font(dataGridView1.Font, FontStyle.Bold);
            }

            for (int counter = 0; counter < (dataGridView1.SelectedCells.Count); counter++)
            {
                dataGridView1.SelectedCells[counter].Style = style;
            }
        }

        //選到的回復預設值
        private void tolsmenuSelectRe_Click(object sender, EventArgs e)
        {
            DataGridViewCellStyle style = new DataGridViewCellStyle();
            {
                style.ForeColor = Color.Black;
                style.Font = new Font(dataGridView1.Font, FontStyle.Regular);
            }
            for (int counter = 0; counter < (dataGridView1.SelectedCells.Count); counter++)
            {

                dataGridView1.SelectedCells[counter].Style = style;
            }
        }

        //全部格子回復預設值
        private void tolsmenuAllRe_Click(object sender, EventArgs e)
        {
            DataGridViewCellStyle style = new DataGridViewCellStyle();
            {
                style.ForeColor = Color.Black;
                style.Font = new Font(dataGridView1.Font, FontStyle.Regular);
            }

            dataGridView1.SelectAll();
            for (int counter = 0; counter < (dataGridView1.SelectedCells.Count); counter++)
            {
                if (dataGridView1.SelectedCells[counter].Value == null) { continue; }
                dataGridView1.SelectedCells[counter].Style = style;
            }
            dataGridView1.ClearSelection();
        }

        #endregion

        #region 對輸入資料做限制的一些事件程式

        //對下一次讀取命令所能讀取的資料數做一些限制 1~127
        private void txbRd2RNum_Leave(object sender, EventArgs e)
        {

            int n = int.Parse(txbRd2RNum.Text);

            if (n > 127)
            {
                txbRd2RNum.Text = "127";
            }
            if (n < 1)
            {
                txbRd2RNum.Text = "1";
            }
        }//end txbRd2RNum_Leave

        //對讀取第一筆資料的位址做一些限制 ADDRESS_MAX ~ ADDRESS_MIN
        private void txbSAddress_Leave(object sender, EventArgs e)
        {


            int n = Convert.ToInt32(txbSAddress.Text, 16);

            if (n > ADDRESS_MAX)
            {
                txbSAddress.Text = Convert.ToString(ADDRESS_MAX, 16).ToUpper();
            }
            if (n < ADDRESS_MIN)
            {
                txbSAddress.Text = Convert.ToString(ADDRESS_MIN, 16).ToUpper();
            }

            txbSAddress.Text = txbSAddress.Text.ToUpper();

        }//end txbSAddress_Leave

        //對要顯示的資料列的位址做一些限制 (開始) 0xE00 ~ txbShowEAddress.text 
        private void txbShowSAddress_Leave(object sender, EventArgs e)
        {

            int n = Convert.ToInt32(txbShowSAddress.Text, 16);

            if (n < 0xE00)
            {
                txbShowSAddress.Text = "E00";
            }
            if (n > Convert.ToInt32(txbShowEAddress.Text, 16))
            {
                txbShowSAddress.Text = txbShowEAddress.Text;
            }

            txbShowSAddress.Text = txbShowSAddress.Text.ToUpper();
        }//end txbShowSAddress_Leave

        //對要顯示的資料列的位址做一些限制 (結束) txbShowSAddress.text ~ 0xE33 
        private void txbShowEAddress_Leave(object sender, EventArgs e)
        {
            int n = Convert.ToInt32(txbShowEAddress.Text, 16);

            if (n > 0xE33)
            {
                txbShowEAddress.Text = "E33";
            }

            if (n < Convert.ToInt32(txbShowSAddress.Text, 16))
            {
                txbShowEAddress.Text = txbShowSAddress.Text;
            }

            txbShowEAddress.Text = txbShowEAddress.Text.ToUpper();
        }//end txbShowEAddress_Leave

        //限制輸入資料為現在的數字基底數
        private void richtxbData_KeyPress(object sender, KeyPressEventArgs e)
        {
            string temp = "";
            temp += e.KeyChar;
            if (!Regex.IsMatch(temp, gpattern))
            {
                e.Handled = true;
            }
        }

        //限制輸入資料為16進位
        private void VerificationNum16(object sender, KeyPressEventArgs e)
        {
            string temp = "";
            temp += e.KeyChar;

            if (!Regex.IsMatch(temp, @"[0-9a-fA-F]"))
            {
                if (e.KeyChar != '\b') e.Handled = true;
            }
        }

        //限制輸入資料為10進位
        private void VerificationNum10(object sender, KeyPressEventArgs e)
        {
            string temp = "";
            temp += e.KeyChar;

            if (!Regex.IsMatch(temp, @"[0-9]"))
            {
                if (e.KeyChar != '\b') e.Handled = true;
            }
        }

        #endregion

    }//end class Form1

    //用來放在commandList裡面的命令類別
    class command4JBus
    {
        public ushort address;
        public byte word2Read;
        public short writeData;
        public string command;

        public command4JBus() { }
        public command4JBus(ushort address, byte word2Read, short writeData, string command)
        {
            this.address = address;
            this.word2Read = word2Read;
            this.writeData = writeData;
            this.command = command;
        }
        
    }//end class command4JBus
}
